package com.lcf.framework.utils;

import jakarta.annotation.Resource;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 描述：Redis缓存工具类（最终版）
 * 特性：1. 支持null值永久缓存 2. 空集合强制存入Redis 3. 不抛任何异常 4. 修复ArrayUtils.filter不存在问题
 *
 * @author lcf
 * @since 2025/2/26 13:01
 */
@Component
public class RedisCacheUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // ========================== 基础对象缓存（支持null永久缓存） ==========================

    /**
     * 缓存对象（支持null值，永久缓存；key无效时不操作）
     */
    public <T> void cache(final String key, T value) {
        // 即使value为null也强制缓存
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存对象（支持null值，带过期时间；参数无效时不操作）
     */
    public <T> void cache(final String key, final T value, final Duration duration) {
        // 允许null值，按指定时长缓存
        redisTemplate.opsForValue().set(key, value, duration);
    }

    /**
     * 获取缓存对象（key无效时返回null）
     */
    public <T> T get(final String key) {
        if (!StringUtils.hasText(key)) {
            return null;
        }
        ValueOperations<String, Object> operation = redisTemplate.opsForValue();
        return (T) operation.get(key);
    }

    // ========================== List 类型缓存（空List强制存入） ==========================

    /**
     * 缓存List（空List也存入Redis；返回实际缓存数量）
     */
    public <T> long cacheList(final String key, final List<T> dataList) {
        if (!StringUtils.hasText(key)) {
            return 0;
        }
        // 处理null列表：视为空List
        List<T> safeList = dataList == null ? Collections.emptyList() : dataList;

        // 先删除旧key，确保类型一致
        redisTemplate.delete(key);

        // 过滤null元素（避免单个null导致序列化问题，但保留空List结构）
        List<T> nonNullList = safeList.stream()
                .filter(Objects::nonNull)
                .toList();

        if (nonNullList.isEmpty()) {
            // 空List：存入一个空List（通过添加后修剪实现）
            redisTemplate.opsForList().rightPush(key, "");
            redisTemplate.opsForList().trim(key, 1, 0);
            return 0;
        }

        // 非空List：正常存入
        Long count = redisTemplate.opsForList().rightPushAll(key, new ArrayList<>(nonNullList));
        return count == null ? 0 : count;
    }

    /**
     * 获取List（key无效时返回空List）
     */
    public <T> List<T> getList(final String key) {
        if (!StringUtils.hasText(key)) {
            return Collections.emptyList();
        }
        if (redisTemplate.hasKey(key)) {
            List<Object> rawList = redisTemplate.opsForList().range(key, 0, -1);
            return rawList == null ? Collections.emptyList() : (List<T>) rawList;
        }

        return Collections.emptyList();
    }

    // ========================== Set 类型缓存（空Set强制存入） ==========================

    /**
     * 缓存Set（空Set也存入Redis）
     */
    public <T> void cacheSet(final String key, final Set<T> values) {
        if (!StringUtils.hasText(key)) {
            return;
        }
        // 处理null集合：视为空Set
        Set<T> safeSet = values == null ? Collections.emptySet() : values;

        // 先删除旧key
        redisTemplate.delete(key);

        // 过滤null元素
        Set<T> nonNullSet = safeSet.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        BoundSetOperations<String, Object> operations = redisTemplate.boundSetOps(key);
        if (nonNullSet.isEmpty()) {
            // 空Set：存入一个空Set（添加后删除临时元素）
            operations.add("");
            operations.remove("");
        } else {
            // 非空Set：正常存入
            nonNullSet.forEach(operations::add);
        }
    }

    /**
     * 向Set添加元素（允许null元素但自动过滤）
     */
    public <T> void addToSet(final String key, final T... values) {
        if (!StringUtils.hasText(key) || ArrayUtils.isEmpty(values)) {
            return;
        }

        T[] nonNullValues = ArrayUtils.removeAllOccurrences(values, null);
        if (ArrayUtils.isNotEmpty(nonNullValues)) {
            redisTemplate.opsForSet().add(key, nonNullValues);
        }
    }

    /**
     * 获取Set（key无效时返回空Set）
     */
    public <T> Set<T> getSet(final String key) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptySet();
        }
        Set<Object> rawSet = redisTemplate.opsForSet().members(key);
        return rawSet == null ? Collections.emptySet() : (Set<T>) rawSet;
    }

    /**
     * 检查元素是否在Set中（参数无效时返回false）
     */
    public <T> boolean isSetMember(final String key, final T value) {
        if (!StringUtils.hasText(key) || value == null || !redisTemplate.hasKey(key)) {
            return false;
        }
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 从Set删除元素（参数无效时不操作）
     */
    public <T> void removeFromSet(final String key, final T... values) {
        if (!StringUtils.hasText(key) || ArrayUtils.isEmpty(values)) {
            return;
        }

        T[] nonNullValues = ArrayUtils.removeAllOccurrences(values, null);
        if (ArrayUtils.isNotEmpty(nonNullValues)) {
            redisTemplate.opsForSet().remove(key, nonNullValues);
        }
    }

    // ========================== Hash 类型缓存（空Hash强制存入） ==========================

    /**
     * 缓存Hash（空Hash也存入Redis）
     */
    public <T> void cacheHash(final String key, final Map<String, T> map) {
        if (!StringUtils.hasText(key)) {
            return;
        }
        // 处理nullMap：视为空Map
        Map<String, T> safeMap = map == null ? Collections.emptyMap() : map;

        // 先删除旧key
        redisTemplate.delete(key);

        // 过滤value为null的Entry
        Map<String, T> nonNullMap = safeMap.entrySet().stream()
                .filter(entry -> entry.getValue() != null)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        if (nonNullMap.isEmpty()) {
            // 空Hash：存入一个空Hash（添加后删除临时字段）
            redisTemplate.opsForHash().put(key, "", "");
            redisTemplate.opsForHash().delete(key, "");
        } else {
            // 非空Hash：正常存入
            redisTemplate.opsForHash().putAll(key, nonNullMap);
        }
    }

    /**
     * 获取Hash（key无效时返回空Map）
     */
    public <T> Map<String, T> getHash(final String key) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptyMap();
        }
        Map<Object, Object> rawMap = redisTemplate.opsForHash().entries(key);
        return rawMap.entrySet().stream()
                .filter(entry -> entry.getKey() instanceof String)
                .collect(Collectors.toMap(
                        entry -> (String) entry.getKey(),
                        entry -> (T) entry.getValue()
                ));
    }

    /**
     * 获取Hash单个字段值（参数无效时返回null）
     */
    public <T> T getHashValue(final String key, final String field) {
        if (!StringUtils.hasText(key) || !StringUtils.hasText(field) || !redisTemplate.hasKey(key)) {
            return null;
        }
        return (T) redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 检查Hash字段是否存在（参数无效时返回false）
     */
    public boolean isHashField(final String key, final String field) {
        if (!StringUtils.hasText(key) || !StringUtils.hasText(field)) {
            return false;
        }
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 从Hash删除字段（参数无效时不操作）
     * 修复：用Stream过滤替代不存在的ArrayUtils.filter
     */
    public void removeHashFields(final String key, final String... fields) {
        if (!StringUtils.hasText(key) || ArrayUtils.isEmpty(fields)) {
            return;
        }

        // 1. 先移除数组中的null元素（用ArrayUtils工具类）
        String[] nonNullFields = ArrayUtils.removeAllOccurrences(fields, null);
        // 2. 再过滤空白字符串（用Stream，替代不存在的ArrayUtils.filter）
        List<String> validFieldList = Arrays.stream(nonNullFields)
                .filter(StringUtils::hasText)
                .toList();

        // 3. 无有效字段时不操作，有则执行删除
        if (!validFieldList.isEmpty()) {
            redisTemplate.opsForHash().delete(key, validFieldList.toArray());
        }
    }

    // ========================== ZSet 类型缓存（空ZSet强制存入） ==========================

    /**
     * 缓存ZSet（空ZSet也存入Redis）
     */
    public <T> void cacheZSet(final String key, final Set<ZSetEntry<T>> values) {
        if (!StringUtils.hasText(key)) {
            return;
        }
        // 处理null集合：视为空ZSet
        Set<ZSetEntry<T>> safeEntries = values == null ? Collections.emptySet() : values;

        // 先删除旧key
        redisTemplate.delete(key);

        // 过滤无效Entry（Entry为null或value为null）
        Set<ZSetEntry<T>> validEntries = safeEntries.stream()
                .filter(entry -> entry != null && entry.value() != null)
                .collect(Collectors.toSet());

        BoundZSetOperations<String, Object> operations = redisTemplate.boundZSetOps(key);
        if (validEntries.isEmpty()) {
            // 空ZSet：存入一个空ZSet（添加后删除临时元素）
            operations.add("", 0);
            operations.remove("");
        } else {
            // 非空ZSet：正常存入
            validEntries.forEach(entry -> operations.add(entry.value(), entry.score()));
        }
    }

    /**
     * 向ZSet添加单个元素（参数无效时不操作）
     */
    public <T> void addToZSet(final String key, final T value, final double score) {
        if (!StringUtils.hasText(key) || value == null) {
            return;
        }
        redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 获取ZSet所有元素（key无效时返回空Set）
     */
    public <T> Set<T> getZSet(final String key) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptySet();
        }
        Set<Object> rawSet = redisTemplate.opsForZSet().range(key, 0, -1);
        return rawSet == null ? Collections.emptySet() : (Set<T>) rawSet;
    }

    /**
     * 按排名范围获取ZSet元素（参数无效时返回空Set）
     */
    public <T> Set<T> getZSetRange(final String key, final long start, final long end) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptySet();
        }
        Set<Object> rawSet = redisTemplate.opsForZSet().range(key, start, end);
        return rawSet == null ? Collections.emptySet() : (Set<T>) rawSet;
    }

    /**
     * 按分数范围获取ZSet元素（参数无效时返回空Set）
     */
    public <T> Set<T> getZSetRangeByScore(final String key, final double min, final double max) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptySet();
        }
        Set<Object> rawSet = redisTemplate.opsForZSet().rangeByScore(key, min, max);
        return rawSet == null ? Collections.emptySet() : (Set<T>) rawSet;
    }

    /**
     * 按分数范围获取ZSet元素（带分数，参数无效时返回空Set）
     */
    public <T> Set<ZSetOperations.TypedTuple<T>> getZSetRangeByScoreWithScores(
            final String key, final double min, final double max) {
        if (!StringUtils.hasText(key) || !redisTemplate.hasKey(key)) {
            return Collections.emptySet();
        }
        Set<ZSetOperations.TypedTuple<Object>> rawTuples = redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
        return rawTuples == null ? Collections.emptySet() : (Set<ZSetOperations.TypedTuple<T>>) (Set<?>) rawTuples;
    }

    /**
     * 获取ZSet元素分数（参数无效时返回null）
     */
    public Double getZSetScore(final String key, final Object value) {
        if (!StringUtils.hasText(key) || value == null || !redisTemplate.hasKey(key)) {
            return null;
        }
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 从ZSet删除元素（参数无效时不操作）
     */
    public <T> void removeFromZSet(final String key, final T... values) {
        if (!StringUtils.hasText(key) || ArrayUtils.isEmpty(values)) {
            return;
        }

        T[] nonNullValues = ArrayUtils.removeAllOccurrences(values, null);
        if (ArrayUtils.isNotEmpty(nonNullValues)) {
            redisTemplate.opsForZSet().remove(key, nonNullValues);
        }
    }

    /**
     * 按分数范围删除ZSet元素（参数无效时返回0）
     */
    public Long removeZSetRangeByScore(final String key, final double min, final double max) {
        if (!StringUtils.hasText(key)) {
            return 0L;
        }
        Long count = redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
        return count == null ? 0L : count;
    }

    /**
     * 按排名范围删除ZSet元素（参数无效时返回0）
     */
    public Long removeZSetRangeByRank(final String key, final long start, final long end) {
        if (!StringUtils.hasText(key)) {
            return 0L;
        }
        Long count = redisTemplate.opsForZSet().removeRange(key, start, end);
        return count == null ? 0L : count;
    }

    // ========================== 通用操作 ==========================

    /**
     * 设置过期时间（参数无效时返回false）
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        if (!StringUtils.hasText(key) || timeout <= 0 || unit == null) {
            return false;
        }
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 删除key（参数无效时返回false）
     */
    public boolean del(final String key) {
        if (!StringUtils.hasText(key)) {
            return false;
        }
        return redisTemplate.delete(key);
    }

    // ========================== ZSet辅助类 ==========================
    public record ZSetEntry<T>(T value, double score) {
    }
}
